/**
 * Copyright 2006 Brad Reynolds
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.leeloo.databinding.internal.ui;
import junit.framework.TestCase;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.jdt.debug.core.IEvaluationRunnable;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaThreadGroup;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.leeloo.databinding.internal.ui.DebugLifeCycle;
import org.leeloo.databinding.internal.ui.IDebugLifeCycleListener;

public class DebugLifeCycleTest extends TestCase {
    IProcess process;

    DebugLifeCycle cycle;

    DebugCycleListener listener;
    
    IBreakpoint breakpoint;

    protected void setUp() throws Exception {
        super.setUp();

        process = new ProcessStub();
        breakpoint = new BreakpointStub();
        cycle = new DebugLifeCycle();
        cycle.setIgnoreBreakpoints(new IBreakpoint[] {breakpoint});
        listener = new DebugCycleListener();
        cycle.addDebugLifeCycleListener(listener);
    }

    public void testStartDebug() throws Exception {
        DebugEvent event = new DebugEvent(process, DebugEvent.CREATE);
        cycle.handleDebugEvents(new DebugEvent[] { event });
        assertEquals("startDebug", listener.events);
    }

    public void testStopDebug() throws Exception {
        DebugEvent event = new DebugEvent(process, DebugEvent.TERMINATE);
        cycle.handleDebugEvents(new DebugEvent[] { event });
        assertEquals("stopDebug", listener.events);
    }

    public void testSuspend() throws Exception {
        IBreakpoint otherBreakpoint = new BreakpointStub();
        IJavaThread thread = new JavaThreadStub(new IBreakpoint[] {otherBreakpoint});
        DebugEvent event = new DebugEvent(thread, DebugEvent.SUSPEND);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("suspend should have been fired", "suspended", listener.events);
        assertTrue("cycle should be suspended", cycle.isSuspended());
        
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("suspend should only have been fired once", "suspended", listener.events);
        
        IBreakpoint[] breakpoints = new IBreakpoint[] {breakpoint};
        cycle.setIgnoreBreakpoints(breakpoints);
        cycle.addDebugLifeCycleListener(listener = new DebugCycleListener());
        thread = new JavaThreadStub(breakpoints);
        event = new DebugEvent(thread, DebugEvent.SUSPEND);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("suspend should not have been fired for our own breakpoing", "", listener.events);
    }
    
    public void testResume() throws Exception {
        assertFalse("cycle should not be suspended", cycle.isSuspended());
        IJavaThread thread = new JavaThreadStub(new IBreakpoint[] {breakpoint});
        DebugEvent event = new DebugEvent(thread, DebugEvent.RESUME);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("resume should not have fired", "", listener.events);
        
        thread = new JavaThreadStub(new IBreakpoint[] {new BreakpointStub()});
        event = new DebugEvent(thread, DebugEvent.SUSPEND);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertTrue("cycle should be suspended", cycle.isSuspended());
        
        event = new DebugEvent(thread, DebugEvent.RESUME, DebugEvent.STEP_INTO);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("resume should not be fired while stepping", "suspended", listener.events);
        
        event = new DebugEvent(thread, DebugEvent.RESUME, DebugEvent.CLIENT_REQUEST);
        cycle.handleDebugEvents(new DebugEvent[] {event});
        assertEquals("resume should fire when not stepping", "suspended resumed", listener.events);
        assertFalse("cycle shoud not be suspended", cycle.isSuspended());
    }

    private class DebugCycleListener implements IDebugLifeCycleListener {
        String events = "";

        public void resumed(DebugEvent[] events) {
            append("resumed");
        }

        public void startDebug(DebugEvent[] events) {
            append("startDebug");
        }

        public void stopDebug(DebugEvent[] events) {
            append("stopDebug");
        }

        public void suspended(DebugEvent[] events) {
            append("suspended");
        }

        private void append(String string) {
            if (events.length() > 0) {
                events += " ";
            }
            events += string;
        }
    }
    
    private class BreakpointStub implements IBreakpoint {
        public void delete() throws CoreException {
            
        }

        public IMarker getMarker() {
            return null;
        }

        public String getModelIdentifier() {
            return null;
        }

        public boolean isEnabled() throws CoreException {
            return false;
        }

        public boolean isPersisted() throws CoreException {
            return false;
        }

        public boolean isRegistered() throws CoreException {
            return false;
        }

        public void setEnabled(boolean enabled) throws CoreException {
            
        }

        public void setMarker(IMarker marker) throws CoreException {
            
        }

        public void setPersisted(boolean registered) throws CoreException {
            
        }

        public void setRegistered(boolean registered) throws CoreException {
            
        }

        public Object getAdapter(Class adapter) {
            return null;
        }        
    }

    /**
     * IJavaThread stub that returns the provided breakpoints from
     * {@link #getBreakpoints()}.
     */
    private class JavaThreadStub implements IJavaThread {
        private final IBreakpoint[] breakpoints;

        public JavaThreadStub(IBreakpoint[] breakpoints) {
            this.breakpoints = breakpoints;
        }

        public boolean canTerminateEvaluation() {
            return false;
        }

        public IJavaVariable findVariable(String variableName) throws DebugException {
            return null;
        }

        public IJavaObject getContendedMonitor() throws DebugException {
            return null;
        }

        public IJavaObject[] getOwnedMonitors() throws DebugException {
            return null;
        }

        public IJavaThreadGroup getThreadGroup() throws DebugException {
            return null;
        }

        public String getThreadGroupName() throws DebugException {
            return null;
        }

        public boolean hasOwnedMonitors() throws DebugException {
            return false;
        }

        public boolean isOutOfSynch() throws DebugException {
            return false;
        }

        public boolean isPerformingEvaluation() {
            return false;
        }

        public boolean isSystemThread() throws DebugException {
            return false;
        }

        public boolean mayBeOutOfSynch() throws DebugException {
            return false;
        }

        public void queueRunnable(Runnable runnable) {

        }

        public void runEvaluation(IEvaluationRunnable evaluation, IProgressMonitor monitor, int evaluationDetail,
                boolean hitBreakpoints) throws DebugException {

        }

        public void stop(IJavaObject exception) throws DebugException {

        }

        public void terminateEvaluation() throws DebugException {

        }

        public IBreakpoint[] getBreakpoints() {
            return breakpoints;
        }

        public String getName() throws DebugException {
            return null;
        }

        public int getPriority() throws DebugException {
            return 0;
        }

        public IStackFrame[] getStackFrames() throws DebugException {
            return null;
        }

        public IStackFrame getTopStackFrame() throws DebugException {
            return null;
        }

        public boolean hasStackFrames() throws DebugException {
            return false;
        }

        public IDebugTarget getDebugTarget() {
            return null;
        }

        public ILaunch getLaunch() {
            return null;
        }

        public String getModelIdentifier() {
            return null;
        }

        public Object getAdapter(Class adapter) {
            return null;
        }

        public boolean canResume() {
            return false;
        }

        public boolean canSuspend() {
            return false;
        }

        public boolean isSuspended() {
            return false;
        }

        public void resume() throws DebugException {

        }

        public void suspend() throws DebugException {

        }

        public boolean canStepInto() {
            return false;
        }

        public boolean canStepOver() {
            return false;
        }

        public boolean canStepReturn() {
            return false;
        }

        public boolean isStepping() {
            return false;
        }

        public void stepInto() throws DebugException {

        }

        public void stepOver() throws DebugException {

        }

        public void stepReturn() throws DebugException {

        }

        public boolean canTerminate() {
            return false;
        }

        public boolean isTerminated() {
            return false;
        }

        public void terminate() throws DebugException {

        }

        public boolean canStepWithFilters() {
            return false;
        }

        public void stepWithFilters() throws DebugException {

        }
    }

    private class ProcessStub implements IProcess {
        public String getAttribute(String key) {
            return null;
        }

        public int getExitValue() throws DebugException {
            return 0;
        }

        public String getLabel() {
            return null;
        }

        public ILaunch getLaunch() {
            return null;
        }

        public IStreamsProxy getStreamsProxy() {
            return null;
        }

        public void setAttribute(String key, String value) {

        }

        public Object getAdapter(Class adapter) {
            return null;
        }

        public boolean canTerminate() {
            return false;
        }

        public boolean isTerminated() {
            return false;
        }

        public void terminate() throws DebugException {

        }
    }
}
